package neo.aoi.octree;

import java.util.Collection;
import java.util.Set;

import javax.swing.SwingWorker;

import neo.aoi.ExportProgressListener;
import neo.aoi.Voxelizer;
import artofillusion.LayoutWindow;
import artofillusion.UndoRecord;
import artofillusion.math.CoordinateSystem;
import artofillusion.math.Vec3;
import artofillusion.object.Cube;
import artofillusion.object.NullObject;
import artofillusion.object.ObjectInfo;
import buoy.widget.BStandardDialog;

/**
 * This class provides the functionality for the VoxelModeller plugin. It
 * extends SwingWorker and calling <code>SwingWorker.execute()</code> causes
 * it to execute its VoxelizeSelected() method in a separate thread.
 * 
 * @see <a
 *      http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html">Concurrency
 *      in Swing</a>
 * 
 * @author Carlos Martinez
 */
public class OctreeModellerTool extends SwingWorker<Void, Void> {
	private ExportProgressListener _progressListener;

	private OctreeExporter.ExportParameters _params;

	private LayoutWindow _window;

	/**
	 * Creates an instance of VoxelModellerTool prepared to run as a SwingWorker
	 * by calling its <code>execute()</code> method.
	 * 
	 * @param params
	 * @param window
	 * @param progress
	 */
	public OctreeModellerTool(OctreeExporter.ExportParameters params,
			LayoutWindow window, ExportProgressListener progress) {
		_progressListener = progress;
		_params = params;
		_window = window;
	}

	/**
	 * Voxelizes the selected objects in the given window. The parameters tol,
	 * res and filling are passed down to the Voxelizer class. The voxelized
	 * versions of the selected objects are inserted in the same window.
	 * 
	 * @param window
	 *            The current LayoutWindow
	 * @param tol
	 *            The specified tolerance as specified by
	 *            artofillusion.object.Object3D.convertToTriangleMesh(double)
	 * @param res
	 *            Resolution of the voxel grid
	 * @param filling
	 *            Indicates whether the voxelization should be used filling the
	 *            inside of the object (CSG method) or just over the polygonal
	 *            faces.
	 * @param progressListener
	 *            Provides the ExportProgressListener to be used by this method
	 *            to report back the progress. This parameter may be null, if no
	 *            progress information is required.
	 * @param depth TODO
	 */
	public static void OctreeSelected(LayoutWindow window, double tol,
			double res, boolean filling, ExportProgressListener progressListener, int depth) {
		Collection<ObjectInfo> objs = window.getSelectedObjects();

		int i = 1;
		for (ObjectInfo obj : objs) {
			if (progressListener != null) {
				progressListener.alertObject(i++, objs.size());
			}

			Set<Vec3> voxels;
			if (filling) {
				voxels = Voxelizer.getVoxelsWithFilling(obj.object, tol, res);
			} else {
				voxels = Voxelizer.getVoxels(obj.object, tol, res);
			}
			addVoxelsToWindow(voxels, res, window, obj.coords, "VOX_"
					+ obj.name);
		}
	}

	private static void addVoxelsToWindow(Set<Vec3> voxels, double vox_size,
			LayoutWindow window, CoordinateSystem coords, String objName) {
		ObjectInfo voxel = new ObjectInfo(
				new Cube(vox_size, vox_size, vox_size), new CoordinateSystem(
						new Vec3(1, 1, 1), 0, 0, 0), "VOXEL");
		CoordinateSystem newcoords = coords.duplicate();
		UndoRecord undo = new UndoRecord(window, false);
		ObjectInfo arrayRoot = new ObjectInfo(new NullObject(), newcoords,
				objName);
		window.addObject(arrayRoot, undo);

		for (Vec3 pos : voxels) {
			ObjectInfo newvox = new ObjectInfo(voxel.object.duplicate(),
					new CoordinateSystem(newcoords.getOrigin().plus(pos), 0, 0,
							0), "Voxel");
			window.addObject(newvox, undo);
			arrayRoot.addChild(newvox, 0);
		}

		window.setUndoRecord(undo);
		window.rebuildItemList();
		window.updateImage();
	}

	@Override
	protected Void doInBackground() throws Exception {
		try {
			OctreeSelected(_window, _params.getTolerance(), _params
					.getMaxResolution(), _params.fill(), _progressListener,_params.getMaxDepth());
			BStandardDialog dialog = new BStandardDialog("Scene Octree Tool",
					"Conversion successfull!", BStandardDialog.INFORMATION);
			dialog.showMessageDialog(_window);
		} catch (Exception e) {
			BStandardDialog dialog = new BStandardDialog("Scene Octree",
					"An error ocurred during the voxelization process: "
							+ e.getMessage(), BStandardDialog.INFORMATION);
			dialog.showMessageDialog(_window);
		}

		return null;
	}

	@Override
	public void done() {
		_progressListener.finished();
	}
}
